#include <type_traits>

#include <iterator/spw_iterator.h>
#include <gtest/gtest.h>
#include <string.h>

class Char {
public:
    using Pointer = char*;
    Char(const char* s) {
        strcpy(ch, s); 
    }
    char* begin() { return ch; }
    const char* str() { return ch; }
private:
    char ch[11];
};

class IterTest: public testing::Test {
public:
    virtual void SetUp() override {
        std::cout << "-----------"<< __func__ << " setup-----------" << std::endl; 
    }

    virtual void TearDown() override { 
        std::cout << "-----------"<< __func__ << " teardown-----------" << std::endl; 
    }
public:
    Char c = "1234567890";
};

TEST_F(IterTest, NormalTest) {
    spwstd::NormalIterator<char*, Char> t;
    spwstd::NormalIterator<char*, Char> t1(c.begin());
    EXPECT_TRUE(*t1++ == '1');
    EXPECT_TRUE(*t1 == '2');
    EXPECT_TRUE((*t1='a') == 'a');
    EXPECT_STREQ(c.str(), "1a34567890");

    spwstd::NormalIterator<char*, Char> t2(t1);
    EXPECT_TRUE(*t2++ == 'a') << *t2;
    EXPECT_TRUE(*(t2+2) == '5') << *t2; // t2=3
    EXPECT_TRUE(t2[1] == '4') << *t2;

    EXPECT_TRUE(*(t2+=2) == '5'); // t2=5
    EXPECT_TRUE(*t2 == '5');
    EXPECT_TRUE(*(2+t2) == '7') << *t2; //
    EXPECT_TRUE(t2 - t1 == 3) << t2-t1; // t2 = 5, t1 = a 

    const spwstd::NormalIterator<const char*, Char> t3(c.begin());
    EXPECT_TRUE(t3 - t2 == -4) << t2-t1; // t3 = 1, t2 = 5 

    EXPECT_FALSE(t1 == t2);
    EXPECT_FALSE(t3 == t2);
    EXPECT_TRUE(t3 != t2);
    EXPECT_TRUE(t1 != t2);
    t1--;
    EXPECT_TRUE(t1 == t3);
    EXPECT_FALSE(t1 != t3);

    spwstd::NormalIterator<char*, Char> t42(c.begin());
    spwstd::NormalIterator<const char*, Char> t4(t2); // t2=5
    EXPECT_TRUE(*t4 == '5') << *t4;

    spwstd::NormalIterator<char*, const Char> ct;

    EXPECT_TRUE((std::is_same<decltype(t42), decltype(ct)>::value==false));
    EXPECT_TRUE((std::is_convertible<spwstd::NormalIterator<char*,Char>, const spwstd::NormalIterator<char*,Char>>::value));
}

TEST(IteratorTraitsTest, Pointertest) {
    // pointer is random iterator
    char test[5] = "1234";
    char *testp = test;
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<char*>::ValueType, char>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<char*>::IteratorCategory, spwstd::RandomAccessIterTag>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<char*>::Pointer, char*>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<char*>::Refer, char&>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<char*>::DifferType, spwconfig::ptrdiff_t>();
    spwstd::advance(testp, 2);
    EXPECT_TRUE(*testp == '3');
    auto next_test = spwstd::next(testp);
    EXPECT_TRUE(*next_test == '4');
    auto prev_test = spwstd::prev(testp);
    EXPECT_TRUE(*prev_test == '2');
    EXPECT_TRUE(*testp == '3');
    auto dis = spwstd::distance(testp, prev_test);
    EXPECT_TRUE(dis == -1);
    testing::StaticAssertTypeEq<decltype(dis), spwstd::IteratorTraits<char*>::DifferType>();

    const char ctest[5] = "5678";
    const char *ctestp = ctest;
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<const char*>::ValueType, char>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<const char*>::IteratorCategory, spwstd::RandomAccessIterTag>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<const char*>::Pointer, const char*>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<const char*>::Refer, const char&>();
    testing::StaticAssertTypeEq<spwstd::IteratorTraits<const char*>::DifferType, spwconfig::ptrdiff_t>();
    spwstd::advance(ctestp, 2);
    EXPECT_TRUE(*ctestp == '7');
    auto cnext_test = spwstd::next(ctestp);
    EXPECT_TRUE(*cnext_test == '8');
    auto cprev_test = spwstd::prev(ctestp);
    EXPECT_TRUE(*cprev_test == '6');
    EXPECT_TRUE(*ctestp == '7');
    auto cdis = spwstd::distance(ctestp, cprev_test);
    EXPECT_TRUE(cdis == -1);
    testing::StaticAssertTypeEq<decltype(cdis), spwstd::IteratorTraits<const char*>::DifferType>();
}
